feat: worktree_ref pinning + SIGHUP graceful shutdown - #30
Merged
Conversation
…ath via SIGHUP Two hardening changes from the 2026-07-31 Observer-Triad closeout arc, plus the MODEL-NOTES correction for the eval rows that arc misclassified. worktree_ref (run-level manifest field, default HEAD): worktree creation happens orchestrator-side and unsandboxed, so a run anchored to an older immutable baseline can pin it for free. The alternative was observed twice in one day: a spec ordering `git checkout` inside the Ringer worktree fails outright under workspace-write (the shared .git/worktrees metadata is outside the writable roots), and the workaround — each worker cloning the repo into its own taskdir — burns tokens on plumbing. Both worktree add sites (run and baseline) take the ref; --detach on both so a branch-name ref cannot fail on "already checked out". Validation refuses the field without worktrees:true. from_path/with_max_parallel rebuilt on dataclass_replace so new Manifest fields cannot be silently dropped by field-copying constructors again. SIGHUP joins SIGINT/SIGTERM in the graceful-shutdown set: a run's parent is often an interactive agent session, and when one died mid-run (network drop) the shell HUP'd the group — the orchestrator died before any flush and left a ghost (state live/unfinished, workers killed, no ERROR verdicts). An INHERITED ignore is respected: `nohup ./ringer.py run` still survives hangups, because installing a handler over SIG_IGN would turn nohup's promise into a graceful shutdown. Mutation-tested at every wired site — four mutations (run-path ref, baseline ref, SIGHUP registration, SIG_IGN respect), four caught. Suite 325 -> 332. MODEL-NOTES: dated correction under gpt-5.6-sol for the three 2026-07-31 FAIL rows stamped failure_class=model that were sandbox-contract and killed-run casualties, with the re-run evidence (3/3 first-try READY). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two hardening changes from the 2026-07-31 Observer-Triad closeout arc, plus the MODEL-NOTES scoreboard correction for the rows that arc misclassified.
worktree_ref — pin the commit task worktrees are created at
Run-level manifest field, default
HEAD(zero behavior change when absent). Worktree creation is orchestrator-side and unsandboxed, so pinning an older immutable baseline is free. Without it, yesterday's dispatch needed a spec-sidegit checkoutinside the Ringer worktree — which fails outright under workspace-write (.git/worktrees/<id>/index.lockis outside the writable roots, cost: two failed dispatches to diagnose) — and the working fallback made each workergit clonethe repo into its own taskdir, burning tokens on plumbing.worktree addsites (run + baseline) take the ref,--detachon both so a branch-name ref can't fail on "already checked out"worktree_refwithoutworktrees: trueis a manifest errorfrom_path/with_max_parallelrebuilt ondataclass_replaceso future Manifest fields can't be silently dropped by field-copying constructorsSIGHUP joins the graceful-shutdown set
A run's parent is often an interactive agent session. When one died mid-run yesterday (network drop), the shell HUP'd the group: the orchestrator died before any flush — ghost state (
live/unfinished forever), workers killed, no ERROR verdicts. SIGHUP now triggers the same graceful path as SIGINT/SIGTERM.nohupsemantics preserved: an inheritedSIG_IGNis respected — installing a handler over it would turn nohup's hangup-survival promise into a graceful shutdown. Tested both ways.Testing
325 → 332, full suite green. Mutation-tested at every wired site — four mutations, four caught:
worktree addreverted toHEADworktree addreverted toHEADSIG_IGNcheck droppedMODEL-NOTES
Dated correction under
gpt-5.6-solfor the three 2026-07-31failure_class=modelFAIL rows that were sandbox-contract and killed-run casualties, with the re-run evidence (same three lanes: 3/3 first-try, READY×3).